package com.song.tools.log.operatelog.aspect;

import com.alibaba.fastjson.JSONObject;
import com.song.tools.core.beans.LogId;
import com.song.tools.core.beans.UserInfo;
import com.song.tools.core.util.ObjectUtils;
import com.song.tools.core.util.UserUtil;
import com.song.tools.log.operatelog.annotation.OLog;
import com.song.tools.log.operatelog.core.LogCustomerConfig;
import com.song.tools.log.operatelog.core.LogRecordContext;
import com.song.tools.log.operatelog.enums.ActionTypeEnum;
import com.song.tools.log.operatelog.enums.ModuleEnum;
import com.song.tools.log.operatelog.service.OperateLogProcessor;
import com.song.tools.log.operatelog.util.SpELParserUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.InitializingBean;

import java.util.*;
import java.util.concurrent.Executor;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 日志切面
 *
 * @author feng
 */
@Aspect
@Slf4j
public class OperateLogAspect implements InitializingBean {
    private static final Pattern BRACES_PATTERN = Pattern.compile("\\{.*?}");

    private static final List<OperateLogProcessor> OPERATE_LOG_PROCESSORS = new ArrayList<>();

    @Resource(name = "logThreadPoolTaskExecutor")
    private Executor executor;

    @Pointcut("@annotation(com.song.tools.log.operatelog.annotation.OLog)")
    private void pointCut() {
    }

    @AfterReturning(value = "pointCut()")
    public void after(JoinPoint joinPoint) {
        try {
            addLog(joinPoint);
        } finally {
            LogRecordContext.clean();
        }
    }

    @AfterThrowing(value = "pointCut()", throwing = "throwable")
    public void afterThrowing(JoinPoint joinPoint, Throwable throwable) {
        log.error("操作日志异常", throwable);
        LogRecordContext.clean();
    }


    public void addLog(JoinPoint joinPoint) {
        String logId = LogId.get();
        UserInfo userInfo = UserUtil.get();
        Map<String, String> logRecordMap = LogRecordContext.get();

        executor.execute(() -> {
            try {
                // 传递logId到异步线程
                LogId.put(logId);

                // 获取方法+入参
                MethodSignature signature = (MethodSignature) joinPoint.getSignature();
                Object[] args = joinPoint.getArgs();

                // 获取注解
                OLog oLog = signature.getMethod().getDeclaredAnnotation(OLog.class);

                // 获取模版中的参数（如果存在参数），并拼接
                List<String> templateParameters = getTemplateParameters(oLog.template());
                buildTemplateData(templateParameters, signature, args, logRecordMap);
                String template = oLog.template();
                for (String templateParameter : templateParameters) {
                    template = template.replace(templateParameter, logRecordMap.get(templateParameter));
                }

                // 获取module
                String module = getModule(oLog, signature, args, logRecordMap);

                // 获取actionType
                String actionType = getActionType(oLog, signature, args, logRecordMap);

                // 获取actionId
                List<String> actionIds = getActionId(oLog, signature, args, logRecordMap);

                // 获取扩展字段
                JSONObject ext = getExt(oLog, signature, args, logRecordMap);

                if (ObjectUtils.isNotEmpty(template)) {
                    for (String actionId : actionIds) {
                        // 日志处理
                        for (OperateLogProcessor operateLogProcessor : OPERATE_LOG_PROCESSORS) {
                            operateLogProcessor.process(userInfo, template, module, actionType, actionId, ext);
                        }
                    }
                } else {
                    log.info("设置日志数据失败：不满足注解条件");
                }
            } catch (Exception e) {
                log.warn("设置日志异常:", e);
            }
        });
    }


    private List<String> getTemplateParameters(String template) {
        List<String> parameters = new ArrayList<>();
        Matcher matcher = BRACES_PATTERN.matcher(template);
        while (matcher.find()) {
            parameters.add(matcher.group());
        }
        return parameters;
    }


    private void buildTemplateData(List<String> parameters, MethodSignature signature, Object[] args, Map<String, String> map) {
        for (String el : parameters) {
            // 如果EL表达式为空，则直接下一个
            if (ObjectUtils.isEmpty(el)) {
                continue;
            }

            String spEl = el;
            // 兼容自定义数据
            spEl = getEl(spEl);
            if (map.containsKey(spEl)) {
                map.put("{" + spEl + "}", map.get(spEl));
                continue;
            }

            // 自定义类处理
            spEl = parseCustomerMethodEl(spEl);

            // El执行
            if (spEl.contains("#")) {
                String value = SpELParserUtils.parse(signature.getMethod(), args, spEl, String.class);
                map.put(el, value);
            } else {
                map.put(el, "");
            }
        }
    }

    private String getModule(OLog oLog, MethodSignature signature, Object[] args, Map<String, String> map) {
        // 设置了module枚举时，优先获取枚举对应的描述
        if (!ModuleEnum.DEFAULT.equals(oLog.module())) {
            return oLog.module().getDesc();
        }

        // 设置了moduleStr时
        if (ObjectUtils.isNotEmpty(oLog.moduleStr())) {
            return oLog.moduleStr();
        }

        // 设置了moduleEl时
        if (ObjectUtils.isNotEmpty(oLog.moduleEl())) {
            try {
                String el = oLog.moduleEl();
                el = getEl(el);
                // 处理自定义的el
                if (map.containsKey(el)) {
                    return map.get(el);
                }

                // 处理自定义方法el
                el = parseCustomerMethodEl(el);

                // 执行el
                return SpELParserUtils.parse(signature.getMethod(), args, el, String.class);
            } catch (Exception e) {
                log.error("日志切面获取module错误", e);
            }
        }
        return null;
    }


    private String getActionType(OLog oLog, MethodSignature signature, Object[] args, Map<String, String> map) {
        // 设置了actionType枚举时，优先获取枚举对应的描述
        if (!ActionTypeEnum.DEFAULT.equals(oLog.actionType())) {
            return oLog.actionType().getDesc();
        }

        // 设置了actionTypeStr时
        if (ObjectUtils.isNotEmpty(oLog.actionTypeStr())) {
            return oLog.actionTypeStr();
        }

        // 设置了actionTypeEl时
        if (ObjectUtils.isNotEmpty(oLog.actionTypeEl())) {
            String el = oLog.actionTypeEl();
            el = getEl(el);
            // 处理自定义的el
            if (map.containsKey(el)) {
                return map.get(el);
            }

            // 处理自定义方法el
            el = parseCustomerMethodEl(el);

            // 执行el
            return SpELParserUtils.parse(signature.getMethod(), args, el, String.class);
        }
        return null;
    }


    private List<String> getActionId(OLog oLog, MethodSignature signature, Object[] args, Map<String, String> map) {
        // 设置了actionIdEl时
        if (ObjectUtils.isNotEmpty(oLog.actionIdEl())) {
            if (map.containsKey(oLog.actionIdEl())) {
                return Collections.singletonList(map.get(oLog.actionIdEl()));
            }
            String el = oLog.actionIdEl();
            el = getEl(el);
            // 处理自定义el
            if (map.containsKey(el)) {
                return Collections.singletonList(map.get(el));
            }

            // 执行el
            return Collections.singletonList(SpELParserUtils.parse(signature.getMethod(), args, el, String.class));
        }

        // 设置了actionIds时
        if (ObjectUtils.isNotEmpty(oLog.actionIds())) {
            String el = getEl(oLog.actionIds());
            if (map.containsKey(el)) {
                return Arrays.asList(map.get(el).split(","));
            }
        }
        return Collections.singletonList(System.currentTimeMillis() * 10 + new Random().nextInt(10000) + "");
    }

    private JSONObject getExt(OLog oLog, MethodSignature signature, Object[] args, Map<String, String> map) {
        // 如果EL表达式为空，则直接结束
        if (ObjectUtils.isEmpty(oLog.ext())) {
            return null;
        }
        String spEl = oLog.ext();
        //兼容自定义数据
        spEl = getEl(spEl);
        if (map.containsKey(spEl)) {
            String value = map.get(spEl);
            if (ObjectUtils.isNotEmpty(value)) {
                try {
                    return JSONObject.parseObject(value);
                } catch (Exception e) {
                    log.info("JSON转换失败:{},{}", value, e.getMessage());
                    return null;
                }
            }
            return null;
        }

        // 自定义类处理
        spEl = parseCustomerMethodEl(spEl);

        // El执行
        if (spEl.contains("#")) {
            String value = SpELParserUtils.parse(signature.getMethod(), args, spEl, String.class);
            if (ObjectUtils.isNotEmpty(value)) {
                try {
                    return JSONObject.parseObject(value);
                } catch (Exception e) {
                    log.info("JSON转换失败:{},{}", value, e.getMessage());
                    return null;
                }
            }
            return null;
        }
        return null;
    }


    private String parseCustomerMethodEl(String el) {
        for (String key : LogCustomerConfig.getCustomerMethod().keySet()) {
            if (el.contains(key)) {
                String className = key.split("\\.")[0];
                el = el.replace(className, "T(" + LogCustomerConfig.getCustomerMethod().get(key) + ")");
            }
        }
        return el;
    }


    private String getEl(String str) {
        str = str.replaceAll("\\{", "");
        str = str.replaceAll("}", "");
        return str;
    }

    public static void addProcessor(OperateLogProcessor operateLogProcessor) {
        log.info("添加操作日志处理器：{}", operateLogProcessor);
        OPERATE_LOG_PROCESSORS.add(operateLogProcessor);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("初始化操作日志切面类");
    }
}
